// 声明状态常量
const PENDING = 'pending' // 默认状态
const FULFILLED = 'fulfilled' // resolve()之后的状态 
const REJECTED = 'rejected' // reject() 之后的状态

class MyPromise {
    // 声明两个私有数组，缓存状态未执行的回调函数
    FULFILLED_CALLBACK_LIST = []
    REJECTED_CALLBACK_LIST = []

    /**
     * 定义私有变量 _status，其对应的属性是构造器中的status
     * 在使用getter、setter监听status时，我们操控 _status
     * 防止死循环
     * （
     *      如果不用一个私有变量存储，那么在每次 对 status进行赋值和取值的操作时，
     *      都会调用 setter和getter，这样就又是一波 赋值取值操作，然后就继续调用 setter和getter，
     *      如此循环往复，成了死循环。
     *      
     *      但是，当使用 _status 私有变量成员时，_status 并不会被getter、setter监听，所以就避免了死循环
     *      
     *  ）
     */
    _status = PENDING

    constructor(fn) {
        this.status = PENDING // 默认状态
        this.value = null
        this.reason = null

        try {
            fn(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }
    }

    /**
     * 注意：
     *  静态成员方法只能通过类名调用
     *  并且在方法体里也只能调用静态方法，没有this
     */
    static resolve(value) {
        // 如果value是一个Promise，直接返回
        if (value instanceof MyPromise) {
            return value
        }

        // 如果不是就直接以value为值，返回一个新的Promise
        return new MyPromise((resolve) => {
            resolve(value)
        })
    }

    static reject(reason) {
        // 以reason为值，返回一个新的Promise
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }

    static all(iterableList) {
        return new MyPromise((resolve, reject) => {
            // 1、判断参数是否是可迭代的
            if (!MyPromise.isIterable(iterableList)) {
                return reject(new TypeError(`${iterableList} is not iterable (cannot read property Symbol(Symbol.iterator))`))
            }
            // 2、转换为数组
            const promiseList = Array.from(iterableList)
            const promiseLength = promiseList.length
            /**
             * 取一个计数器，每次执行resolve回调时就+1
             * 并声明一个返回值的数组，每次resolve时就将返回值存进去，
             * 只有当计数器的长度和promise个数相等时，即所有promise都成功执行了，
             * 然后就将存成功结果的数组resolve出去
             */
            let resolvedCount = 0
            let resolvedValues = new Array(promiseLength)
            // 如果是空数组，直接返回空数组
            if (promiseLength === 0) {
                return resolve([])
            } else {
                // 3、同步执行
                for (let i = 0; i < promiseLength; i++) {
                    // 4、全部转换为promise对象
                    MyPromise.resolve(promiseList[i]).then(
                        (value) => {
                            // 成功时就+1
                            resolvedCount++
                            // 5、存入对应的value到结果数组中
                            resolvedValues[i] = value
                            if (resolvedCount === promiseLength) {
                                return resolve(resolvedValues)
                            }
                        },
                        (reason) => {
                            // 6、一旦有失败的，就将失败结果reject出去
                            return reject(reason)
                        },
                    )

                }
            }
        })
    }

    // 直接调用then方法，不传成功的回调，只传失败的回调即可
    catch(onRejected) {
        return this.then(null, onRejected)
    }

    static race(iterableList) {
        return new MyPromise((resolve, reject) => {
            /** 
             * 判断传入的参数是否可迭代 
             * */
            if (!MyPromise.isIterable(iterableList)) {
                return reject(new TypeError(`${iterableList} is not iterable (cannot read property Symbol(Symbol.iterator))`))
            }

            // 2、将类数组转换为数组
            const promiseList = Array.from(iterableList)
            const promiseLength = promiseList.length

            // 如果是一个空数组，直接resolve一个空数组
            if (promiseLength === 0) {
                return resolve([])
            } else {
                // 3、同步执行数组中的Promise
                for (let i = 0; i < promiseLength; i++) {
                    // 4、为了防止某一个参数不是Promise，直接全转换一下
                    MyPromise.resolve(promiseList[i]).then(
                        (value) => {
                            // 5、一旦有结果了，直接返回
                            return resolve(value)
                        },
                        (reason) => {
                            // 5、
                            return reject(reason)
                        }
                    )
                }
            }
        })
    }

    /**
     * @description 判断value是否可迭代
     * @param {*} value 
     * @returns {Boolean} true：可迭代；false：不可迭代
     */
    static isIterable(value) {
        // 如果是空或undefined 直接返回false
        if (value === null || value === undefined) {
            return false
        } else {
            // 对象里如果没有Symbol.iterator，默认是不可迭代的
            // 可迭代的对象都会默认实现Symbol.iterator迭代器
            return !(value[Symbol.iterator] === undefined)
        }
    }


    resolve(value) {
        if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason
            this.status = REJECTED
        }
    }

    get status() {
        return this._status
    }

    /**
     * 监听状态的改变，如果变成了fulfilled或者rejected，
     * 就从缓存回调事件的数组中，把该执行的回调挨着执行一遍
     */
    set status(newStatus) {
        this._status = newStatus

        switch (newStatus) {
            case FULFILLED:
                this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                    callback(this.value)
                });
                break;
            case REJECTED:
                this.REJECTED_CALLBACK_LIST.forEach(callback => {
                    callback(this.reason)
                });
                break;
        }

    }


    then(onFulfilled, onRejected) {
        /**
         * 校验 onFulfilled, onRejected
         * 如果是函数，就还用自己
         * 如果不是函数，我们就忽略，给个默认的函数
         */
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) => {
            throw reason
        }

        const promise2 = new MyPromise((resolve, reject) => {

            // 使用queueMicrotask改造成微任务
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    // 使用catch包裹，一旦出错就reject出去
                    try {
                        // 返回值我们定义为 x
                        const x = realOnFulfilled(this.value)
                        /**
                         * 处理返回值的方法，我们定义为 resolvePromise
                         * 
                         * 为什么参数会多传递个 当前的返回值promise2呢？
                         *  这是因为当 x 也是一个Promise时，
                         *      把自己当前返回的promise2也传递过去，是为了校验 x === promise2的情况，
                         *      如果完全是一个promise，会导致死循环
                         * 
                         * */
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }

            // 使用queueMicrotask改造成微任务
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    // 使用catch包裹，一旦出错就reject出去
                    try {
                        // 返回值我们定义为 x
                        const x = realOnRejected(this.reason)
                        // 处理返回结果
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }

            switch (this.status) {
                case FULFILLED:
                    // 此处改为调用对应的微任务
                    fulfilledMicrotask()
                    break;
                case REJECTED:
                    // 此处改为调用对应的微任务
                    rejectedMicrotask()
                    break;
                case PENDING:
                    // 此处缓存数组改为存微任务
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                    break;
            }
        })
        return promise2
    }

    // 处理回调函数中的返回值，会有大量的if操作，因为有很多种情况，都应该考虑到
    resolvePromise(promise2, x, resolve, reject) {
        // 1、如果 .then()返回的promise和 执行完回调返回值是同一个promise
        if (x === promise2) {
            // 不能相等，如果相等会死循环，应保证每次执行完返回的promise的唯一性
            return reject(new TypeError('The promise and the return value are the same'))
        }

        // 2、如果返回值x是promise
        if (x instanceof MyPromise) {
            // 微任务中执行，继续解析Promise
            queueMicrotask(() => {
                x.then(
                    (y) => {
                        // 递归解析，一直解析成其他情况为止
                        this.resolvePromise(promise2, y, resolve, reject)
                    },
                    reject,
                )
            })
        } else if (typeof x === 'object' || this.isFunction(x)) { // 3、如果返回值是对象或者函数
            // 先判断是不是null
            if (x === null) {
                return resolve(x) // 直接返回结果
            }

            // 在看看对象里有没有符合promise规范的then方法
            let then = null
            try {
                // 把这个then 取出来，万一报错了，直接reject出去
                then = x.then
            } catch (e) {
                reject(e)
            }

            if (this.isFunction(then)) {
                // 成功和失败的回调只能调用一个
                let called = false
                // 执行方法的时候包裹一个try..catch... 捕获方法里的异常
                try {
                    // 如果是x.then是function
                    then.call(
                        x,
                        (y) => {
                            if (called) return
                            called = true
                            // 继续解析返回的结果
                            this.resolvePromise(promise2, y, resolve, reject)
                        },
                        (r) => {
                            if (called) return
                            called = true
                            // 直接reject回去
                            reject(r)
                        }
                    )
                } catch (e) {
                    if (called) return
                    called = true
                    // 直接reject回去
                    reject(r)
                }
            } else { // x是对象，且x.then不是function || x直接就是一个function
                resolve(x) // 直接resolve出去
            }
        } else { // 4、如果什么都不是，那只能是基本类型了，直接把结果resolve出去
            resolve(x)
        }
    }

    // 判断参数是否为函数
    isFunction(param) {
        return typeof param === 'function'
    }
}

/* let test = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111)
        reject(111)
    }, 1000)
}).then(
    (value) => {
        console.log(value, 'fulfilled')
    },

    (reason) => {
        console.log(reason, 'rejected')
    },

) */

/* const test = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111)
    }, 1000)
}).then(console.log)

console.log(test) */

/* const test = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111)
    }, 1000)
})

test.then(console.log)
test.then(console.log)
test.then(console.log)
test.then(console.log) */


/* const test = MyPromise.resolve(111)
test.then(console.log) */

const test = MyPromise.resolve(
    new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(111)
        }, 1000)
    })
)

test.then(console.log)
console.log(test)